<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
 
/**
 * Prunus Core Library
 *
 * Core de Prunus
 *
 * @author		agosto
 * @date		  20090504
 * @version		0.1
 * @copyright	Copyright (c) 2009, 7dedos
 * @link		http://www.7dedos.com.ar
 */
 
/**
 * Prunus Library
 *
 * Core de Prunus
 *
 * @author		agosto
 * @date		  20091212
 * @version		0.2
 * @copyright	Copyright (c) 2009, 7dedos
 * @link		http://www.7dedos.com
 */
 
class PrunusLib {
	protected $ci;
	public $idioma = '';
	public $usuId = 0;
	public $usuName = '';
	public $usuFullName = '';
  public $resultado = '';
  public $currentAction = 0;
  public $currentMenuItem = 0;
  private $usuMenId = 0;
  private $usuMenMeiId = 0;
  private $menuArray = array();
  private $yaValideAccionActual = FALSE;

	public function __construct()	{
		$this->ci =& get_instance();
    $this->ci->load->config('prunus');
    $this->ci->output->enable_profiler($this->ci->config->item('PR_show_profiler'));  
    $this->ci->load->library('session');
    $this->ci->load->helper('language');
    $this->ci->load->helper('html');
    $this->ci->load->helper('url');
    
    $this->yaValideAccionActual = FALSE;
    $this->resultado='';
    if (! ($this->usuId = $this->ci->session->userdata('usuId'))) { $this->usuId=0; };
    $this->ci->load->model('PrunusModel');
    
    if ($this->is_logged_in()) { 
      //levanto de la base de datos la info del usuario
      $this->_getUserData($this->usuId,FALSE);
      
    } else { //usuario no logueado, intento levantar de la cookie local el idioma de preferencia
      $this->ci->load->helper('cookie');
      $resultado = get_cookie($this->ci->config->item('PR_cookie_offline_language'), TRUE);
      if ($resultado!=FALSE) { $this->switchIdioma($resultado); } else { $this->switchIdioma($this->ci->config->item('language')); }
      $this->usuName = '';
      $this->usuFullName = '';
      $this->usuMenId = 0;
      $this->menuArray = array();
      if (! $this->_isPublicZone()) redirect('login', 'location', 301);
      
    }
		log_message('debug', 'PrunusLib inicializada');
	}
  
/* INTERFAZ PRIVADA *********************************************************************************************************/
	private function _salt() { return substr(md5(uniqid(rand(), true)), 0, $this->ci->config->item('PR_salt_length')); }
  
  private function _isPublicZone() {
    switch (strtolower($this->ci->uri->segment(1,''))) {
      case "prunus":
        switch (strtolower($this->ci->uri->segment(2,''))) {
          case "idioma": 
          case "auth": return(TRUE); break;
          default: return(FALSE);
        }
        break;
      case "login":
        return (strtolower($this->ci->uri->segment(2,''))=='');
        break;
      default: return(FALSE);
    }
  }
  
  private function _getUserData($usuId=0,$bSaveIdioma = TRUE) {
    $this->resultado='';
    if ($usuId === 0) $usuId = $this->usuId;
    $data = $this->ci->PrunusModel->getUser($usuId)->row();
    $this->usuName = $data->usu_name;
    $this->usuMenId = $data->usu_men;
    $this->usuMenMeiId = $data->men_mei;
    $this->menuArray = $this->buildMenu($this->usuMenMeiId);
    if ($data->usu_lastName != '') {
      $this->usuFullName = $data->usu_lastName;
      if ($data->usu_firstName != '') $this->usuFullName .= ',' . $data->usu_firstName;
    } else {
      if ($data->usu_firstName != '') $this->usuFullName = $data->usu_firstName;
    }
    $this->switchIdioma($data->usu_idioma, $bSaveIdioma);
  }
  
  private function _getUsuIdFromUsernameAndPassword ($userName,$password) {
    $usuId = $this->ci->PrunusModel->getUsuIdFromName($userName);
    if ($usuId != 0 ) {
      $hash = $this->ci->PrunusModel->getUsuPwdHah($usuId);
      if ($hash != '') {
        $salt_length = $this->ci->config->item('PR_salt_length');
        $salt = substr($hash, 0, $salt_length);
        $password = $salt . substr(sha1($salt . $password), 0, -$salt_length);
        if ($hash !== $password) $usuId = 0;
      } else {
        $usuId = 0;
      }
    } else {
      $usuId = 0;
    }
    return ($usuId);
  }

  private function _getMenuULHijos ($meiId, $classCurrentLI = '', $classLI = '') {
    //busco meiId - lo agrego al string - llamo recursivamente hijos
    $item = $this->menuArray[$meiId];
    
    if ($meiId != $this->currentMenuItem) {
      if ($classLI != '') $class = " class=\"". $classLI . "\""; else $class='';
      $out = "<li><span" . $class . ">" . anchor($this->menuArray[$meiId]["uri"],$this->menuArray[$meiId]["name"]) . "</span>";
    } else {
      if ($classCurrentLI !== '') $class = " class=\"". $classCurrentLI . "\""; else $class=" class=\"". $classLI . "\"";
      $out = "<li><span" . $class . ">" . anchor($this->menuArray[$meiId]["uri"],$this->menuArray[$meiId]["name"]) . "</span>";
    }
    
    if (count($this->menuArray[$meiId]["children"])>0) $out .= "<ul>";
    foreach ($this->menuArray[$meiId]["children"] as $child) {
      $out .= $this->_getMenuULHijos($child, $classCurrentLI, $classLI);
    }
    if (count($this->menuArray[$meiId]["children"])>0) $out .= "</ul>";
    $out .= "</li>";
    return ($out);
  }

  private function _buildMenuHijos (&$res,&$menu,$meiId) {
    //busco los hijos. los agrego como hijos de meiId y llamo recursivamente hasta q no tenga mas hijos
    //busco hijos
    foreach($res as $row) {
      if ($row->mei_mei == $meiId) {
        $menu[$meiId]["children"][] = $row->mei_id;
        $menu[$row->mei_id] = array("parent"=>$meiId, "uri"=>$row->mei_uri, "name"=>$this->getMenuItemName($row->mei_id), "children"=>array());
        $this->_buildMenuHijos($res, $menu, $row->mei_id);
      }
    }
  }








  
/* INTERFAZ PUBLICA *********************************************************************************************************/
	
  /* get indented ***************************************************************
  parametros: in  $cant        cantidad de espacios a indentar
              in  $msg         mensaje a procesar
              in  $skipNewLine bool. si es true, no agrega \n al final
  *******************************************************************************/
  public function gi ($cant,$msg,$skipNewLine=FALSE) { $out = str_pad('',$cant," ") . $msg; if ($skipNewLine) return $out; else return ($out . "\n"); } 

  /* echo indented **************************************************************
  parametros: in  $cant        cantidad de espacios a indentar
              in  $msg         mensaje a imprimir en el echo
  *******************************************************************************/
  public function ei ($cant,$msg) { echo str_pad('',$cant," ") . $msg . "\n"; } 

	public function is_logged_in() { return ($this->usuId!=0); }
  

  public function securityHook() { if ($this->is_logged_in()) { if (! $this->_isPublicZone()) { if (! $this->yaValideAccionActual) { if (! $this->validarPermisos()) { redirect('static/permisos.php?atn=' . $this->currentAction); } } } } }
  
  public function setCurrentAction($accId) { $this->currentAction = $accId; return $this->validarPermisos(); }
  
  public function setCurrentMenuItem($meiId) { $this->currentMenuItem = $meiId; }
  
  public function getCurrentPageTitle($siteTitle="") {
    if ($siteTitle == "") { $out = $this->ci->config->item('PR_site_title'); } else { $out = $siteTitle; }
    $an = $this->getActionName();
    if ($an != '') $out .= $this->ci->config->item('PR_siteSeparator') . $an;
    return $out;
  }
  
  public function getActionName($accId=0) {
    $this->ci->lang->load('prunus_acciones', $this->idioma);
    if ($accId != 0) {
      return (lang('PR_acciones_' . $accId));
    } else {
      return (lang('PR_acciones_' . $this->currentAction));
    }
  }

  public function getMenuItemName($meiId = 0) {
    $this->ci->lang->load('prunus_menus_items', $this->idioma);
    if ($meiId != 0) {
      return (lang('PR_menus_items_' . $meiId));
    } else {
      return (lang('PR_menus_items_' . $this->currentMenuItem));
    }
  }

  /* getBreadcrumb ***************************************************************
  arma los breadcrumb a partir del menu y el item actual
  *******************************************************************************/
  public function getBreadcrumb($divId = 'breadcrumbs') {
    $out = anchor($this->menuArray[$this->currentMenuItem]["uri"],$this->menuArray[$this->currentMenuItem]["name"]);
    $parent = $this->menuArray[$this->currentMenuItem]["parent"];
    while ($parent > 0) {
      $out = anchor($this->menuArray[$parent]["uri"],$this->menuArray[$parent]["name"]) . $this->ci->config->item('PR_breadcrumbSeparator') . $out;
      $parent = $this->menuArray[$parent]["parent"];
    }
    return ("<div id=\"" . $divId . "\">" . $out . "</div>");
  }
  
  //devuelvo true si el usuario esta logueado y tiene permisos para la accion $accId
  //si la accId viene en 0, tomo la accion cargada actualmente  
  public function validarPermisos($accId = 0) {
    //permiso positivo = 1
    //permiso negativo = -1
    //no definido      = 0
    //acumulo permiso de roles en $perR (resolviendo la colision de roles) y el de usuario en $perU
    //finalmente evaluo ambos permisos (usuario y rol) y resuelvo la colision usuario-rol)
    
    //convierto el permiso de la base al de variables haciendo *2 - 1
    
    $this->resultado='';
    if ($accId == 0) $accId = $this->currentAction;
    if ( $this->currentAction == $accId) $this->yaValideAccionActual = TRUE;
    if ($this->is_logged_in()) {
      $data = $this->ci->PrunusModel->getUsuPermisosAccion($this->usuId, $accId);
      $perR = 0;
      $perU = 0;
      $per  = 0; //permiso final
      foreach ($data->result() as $row) {
        if ($row->rol == 1) {
          if ($perR==0) { //si es el primer permiso, no valido contra colisiones. simplemente lo guardo.
            $perR = ($row->permiso * 2 - 1);
          } else {
            if ($perR != ($row->permiso * 2 - 1)) { //si el permiso leido es distinto al actual, hago algo, sino lo ignoro
              if ($this->ci->config->item('PR_seguridad_colision_roles')) {
                if ($perR!=1) $perR = 1; //si el actual es negativo y gana el positivo, como eran distintos, guardo el positivo
              } else {
                if ($perR!=-1) $perR = -1; //si el actual es positivo y gana el negativo, como eran distintos guardo el negativo
              }
            }
          }
        } else {
          //por usuario-accion solo se puede asignar una vez el valor, asi q aca no hay colisiones posibles
          $perU = ($row->permiso * 2 - 1);
        }
      }
      
      if (($perR !=0) && ($perU !=0)) {
        if ($perR != $perU) { //si hay colision usuario-rol...
          if ($this->ci->config->item('PR_seguridad_colision_usuario_rol')) {
            $per = $perU;
          } else {
            $per = $perR;
          }
        } else {
          $per = $perR;
        }
      }

      if (($perR == 0) && ($perU != 0)) $per = $perU;
      if (($perR != 0) && ($perU == 0)) $per = $perR;
      if (($perR == 0) && ($perU == 0)) $per = 0;
      return ($per == 1);
    } else {
      return FALSE; //no hay permisos porque el usuario no esta logueado
    }
  }
	
  //cambia el idioma del usuario en la sesion y si esta logueado en db
  public function switchIdioma($idioma, $bSave = TRUE) {
    $this->resultado='';
    $this->idioma = $idioma;
    if ($this->is_logged_in()) {
      if ($bSave) $this->ci->PrunusModel->setUsuIdioma($this->usuId, $this->idioma); //grabo en db el nuevo idioma del usuario
    } else {
      $this->ci->load->helper('cookie');
      set_cookie($this->ci->config->item('PR_cookie_offline_language'), $this->idioma, 86500); //grabo el valor en la cookie
    }
    $this->ci->lang->load('prunus', $this->idioma); //re cargo el archivo de idioma en el idioma actual
    $this->ci->config->set_item('language', $this->idioma);
  }

  
/* USUARIOS *****************************************************************************************************************/
	public function deleteUser($usuId) {
    if ($usuId === 0) $usuId = $this->usuId;

    $data = $this->ci->PrunusModel->getUser($usuId)->row();
    //EVENTLOG
    $this->logEvent($this->ci->config->item('PR_evento_DeleteUser'),$data->usu_name,$usuId);
    
    $this->ci->PrunusModel->deleteUser($usuId);
  }
	public function killUser($usuId) {
    if ($usuId === 0) $usuId = $this->usuId;

    $data = $this->ci->PrunusModel->getDeletedOrNotUser($usuId)->row();
    //EVENTLOG
    $this->logEvent($this->ci->config->item('PR_evento_KillUser'),$data->usu_name,$usuId);
    
    $this->ci->PrunusModel->killUser($usuId);
  }
  public function insertUserRole($usuId,$rolId) {
    $this->ci->PrunusModel->insertUserRole($usuId,$rolId);
  }
	public function login($userName,$password) {
    $this->resultado='';
    $this->usuId = $this->_getUsuIdFromUsernameAndPassword ($userName,$password);
    if ($this->usuId == 0) {
      $this->resultado=lang('pr_LoginInvalido');
    } else {
      $this->_getUserData();
      set_cookie($this->ci->config->item('PR_cookie_last_user'), $this->usuName, 86500); //grabo el valor en la cookie
    }
    $this->ci->session->set_userdata('usuId', $this->usuId);  
    
    //EVENTLOG
    $this->logEvent($this->ci->config->item('PR_evento_Login'));
    return ($this->usuId!=0);
	}

  public function changePassword($oldPassword,$newPassword) {
    $this->resultado='';
    if ($this->_getUsuIdFromUsernameAndPassword ($this->usuName,$oldPassword) === $this->usuId ) { //la pwd anterior es correcta
      $salt_length       = $this->ci->config->item('PR_salt_length');
      $salt = $this->_salt();
      $hash = $salt . substr(sha1($salt . $newPassword), 0, -$salt_length);
      $this->ci->PrunusModel->setUsuPwdHash($this->usuId, $hash);
      //EVENTLOG
      $this->logEvent($this->ci->config->item('PR_evento_ChangePwd'));
      return TRUE;
    } else {
      $this->resultado=lang('pr_ErrorChangePassword');    return FALSE;
    }
  }
  public function setUserData($firstName=null,$lastName=null) {
    $this->resultado='';
    if ($firstName !== null) $this->ci->PrunusModel->setUsuFirstName(0, $firstName);
    if ($lastName !== null) $this->ci->PrunusModel->setUsuLastName(0, $lastName);
  }
	
	public function logout() {
    $this->resultado='';
    //EVENTLOG
    $this->logEvent($this->ci->config->item('PR_evento_Logout'));
    $this->ci->session->unset_userdata('usuId');
		$this->ci->session->sess_destroy();  
    $this->usuId = 0;
	}
  
  public function register($name,$idioma,$firstName,$lastName,$pwd) {
    $salt_length       = $this->ci->config->item('PR_salt_length');
    $salt = $this->_salt();
    $password = $salt . substr(sha1($salt . $pwd), 0, -$salt_length);
    $newId = $this->ci->PrunusModel->insertUser($name,$idioma,$firstName,$lastName,$password);
    //EVENTLOG
    if ($newId > 0) $this->logEvent($this->ci->config->item('PR_evento_RegisterUser'),$name,$newId);
    return ($newId);
  } //register
  public function getUserRoles($usuId=0, $exclusionArray='') {
    $this->resultado='';
    $out = array();
    
    $this->ci->lang->load('prunus_roles', $this->idioma);
    $data = $this->ci->PrunusModel->getUsuRoles($usuId,$exclusionArray);
    foreach ($data->result() as $row) {
      if ($row->rol_builtin != 1) {
        $out[] = array('rol_id' => $row->rol_id, 'rol_name' => $row->rol_name, 'rol_builtin' => $row->rol_builtin);
      } else {
        $out[] = array('rol_id' => $row->rol_id, 'rol_name' => lang('PR_roles_' . $row->rol_id), 'rol_builtin' => $row->rol_builtin);
      }
    }
    return ($out);
  } //getUserRoles
  public function setUserRoles ($usuId,$rolesList) {
    $this->ci->PrunusModel->emptyUserRoles($usuId); //saco todos los roles
    $rolesNames = '';
    $roles = explode(",", $rolesList);
    foreach ($roles as $rol) { //inserto uno a uno los de la lista
      if ($rol != '') {
        $this->ci->PrunusModel->insertUserRole($usuId,$rol);
        $data = $this->ci->PrunusModel->getRole($rol)->row();
        $rolesNames .= $data->rol_name . ',';
      }
    }
    if (strlen($rolesNames) > 0) $rolesNames = substr($rolesNames,0,-1);
    //EVENTLOG
    $this->logEvent($this->ci->config->item('PR_evento_SetUserRoles'),$rolesNames,$rolesList);
  } //setUserRoles

  public function checkUserHasRole($usuId,$rolId) {
    $this->resultado='';
    $out = FALSE;
    $data = $this->ci->PrunusModel->getRoles($usuId);
    foreach ($data->result() as $row) {
      if ($row->rol_id==$rolId) $out = TRUE;
    }
    return $out;
  }
/****************************************************************************************************************************/



/* ROLES ********************************************************************************************************************/
  public function insertRole($name) {
    $newId = $this->ci->PrunusModel->insertRole($name);
    //EVENTLOG
    if ($newId > 0) $this->logEvent($this->ci->config->item('PR_evento_CreateRole'),name,$newId);
    return ($newId);
  }
  public function deleteRole($rolId) {
    $data = $this->ci->PrunusModel->getRole($rolId)->row();
    //EVENTLOG
    $this->logEvent($this->ci->config->item('PR_evento_DeleteRole'),$data->rol_name,$rolId);
    $this->ci->PrunusModel->deleteRole($rolId);
  }
  public function killRole($rolId) {
    $data = $this->ci->PrunusModel->getDeletedOrNotRole($rolId)->row();
    //EVENTLOG
    $this->logEvent($this->ci->config->item('PR_evento_KillRole'),$data->rol_name,$rolId);
    $this->ci->PrunusModel->killRole($rolId);
  }
  public function getRoles($limit=0, $offset=0, $exclusionArray='') {
    $this->resultado='';
    $out = array();
    
    $this->ci->lang->load('prunus_roles', $this->idioma);
    $data = $this->ci->PrunusModel->getRoles($limit,$offset,$exclusionArray);
    
    foreach ($data->result() as $row) {
      if ($row->rol_builtin != 1) {
        $out[] = array('rol_id' => $row->rol_id, 'rol_name' => $row->rol_name, 'rol_builtin' => $row->rol_builtin);
      } else {
        $out[] = array('rol_id' => $row->rol_id, 'rol_name' => lang('PR_roles_' . $row->rol_id), 'rol_builtin' => $row->rol_builtin);
      }
    }
    return ($out);
  } //getRoles
/****************************************************************************************************************************/



/* EVENTOS ******************************************************************************************************************/
  public function logEvent($eveId,$p1='',$p2='',$p3='',$p4='',$p5='') {
    $sql = "select eve_gre";
    $sql .=" from pr_eventos";
    $sql .=" where eve_id=" . $eveId;
    $query = $this->ci->db->query($sql);
    if ($query->num_rows() == 1) {
      $greId = $query->row()->eve_gre;
      if (array_search($greId,$this->ci->config->item('PR_seguridad_eventlog_grupos')) !== FALSE) {
        $this->ci->PrunusModel->insertEvent($this->usuId,$eveId,$p1,$p2,$p3,$p4,$p5);
      }
    } else {
      log_message('error', 'No se pudo generar un registro en el log de eventos (' . $eveId . ')');  
    }
  }
  
  public function getEventsFromUser($usuId = 0, $from = null, $to = null, $eveArray = null, $greArray = null) {
    if ($usuId === 0) $usuId = $this->usuId;
    if (is_null($eveArray)) {
      $eveList = null;
    } else {
      $eveList = '';
      foreach ($eveArray as $eve) $eveList .= $eve;
      $eveList = substr($eveList,0,-1);
    }
    if (is_null($greArray)) {
      $greList = null;
    } else {
      $greList = '';
      foreach ($greArray as $gre) $greList .= $gre;
      $greList = substr($greList,0,-1);
    }
    return ($this->ci->PrunusModel->getEventsFromUser($usuId, $from, $to, $eveList, $greList));
  }
/****************************************************************************************************************************/
  
/* MENUS *******************************************************************/
  public function buildMenu($meiId) {
    $menu = array();
    $res = $this->ci->PrunusModel->getMenuItems()->result();
    //busco el root
    foreach($res as $row) {
      if ($row->mei_id == $meiId) {
        $menu[$row->mei_id] = array("parent"=>0, "uri"=>$row->mei_uri, "name"=>$this->getMenuItemName($row->mei_id),"children"=>array());
      }
    }
    $this->_buildMenuHijos($res, $menu, $meiId);
    return ($menu);
  }
  
  public function getMenuUL($classUL = '', $currentLI = '', $classLI = '') {
    if ($classUL !== '') {
      return "<ul class=\"$classUL\">" . $this->_getMenuULHijos($this->usuMenMeiId, $currentLI, $classLI) . "</ul>";
    } else {
      return "<ul>" . $this->_getMenuULHijos($this->usuMenMeiId, $currentLI, $classLI) . "</ul>";
    }
  }
/***************************************************************************/

}